﻿using System;
using libEngineAI.Interfaces;
using System.Collections.Generic;
using System.Linq;
using log4net; 
using System.Diagnostics;

namespace libEngineAI.Engines
{
	public class MiniMaxEngine : IEngine, IPlayer
	{
		#region IEngine implementation

		public int Level { get; set; }

		public TeamEnum Team { get; set; }

		public Movement Search( IGame Game )
		{
			_NMoves = 0;
			_MejorJugada = null;

			MiniMaxSearch( Game, Level );

			return _MejorJugada;
		}

		public int Evaluate( IGame Game )
		{
			return Game.Evaluate() * (int) Team;
		}

		#endregion

		#region IPlayer implementation

		public Movement GetMovement( IGame Game )
		{
			return Search( Game );
		}

		public PlayerType PlayerType { get; set; }

		#endregion

		private Movement _MejorJugada = null;
		private long _NMoves = 0;

		//private static readonly ILog log = LogManager.GetLogger( typeof( MiniMaxEngine ) );

		private int MiniMaxSearch( IGame Game,int depth )
		{
			int mejor, puntaje;

			TeamEnum winner = Game.Winner();
			if( winner != TeamEnum.NONE )
				return (int.MaxValue -1) * ( int )Team * ( int )winner;

			if (depth <= 0)   
			{
				return Evaluate( Game );
			} 

			if( Game.Turn == Team )
				mejor = -int.MaxValue;
			else
				mejor = int.MaxValue;

			//Stopwatch sw = new Stopwatch();
			//sw.Reset();
			//sw.Start();
			IEnumerable<Movement> Moves_prev = Game.GenerateMoves();
			//sw.Stop();
			//log.DebugFormat( "Tiempo en obtener movimientos: {0}", sw.Elapsed );

			// Optimization: If we only havee 1 move, just move
			if( Moves_prev.Count() == 1 && depth == Level )
			{
				_MejorJugada = Moves_prev.First();
				return int.MaxValue;
			}

			//Stopwatch sw = new Stopwatch();
			//sw.Start();

			// With the next block we got walk the loop in the same order if is black or white( time: 0,00000003 s)
			IEnumerable<Movement> Moves;
			if( Team == TeamEnum.WHITE )
				Moves = Moves_prev.Reverse();
			else
				Moves = Moves_prev;

			//sw.Stop();
			//log.DebugFormat( "NMoves  = {0} -- depth = {1} -- GeneratingMoves: {2}", _NMoves, depth, sw.Elapsed );

			foreach( Movement m in Moves )
			{
				++_NMoves;

				Game.Board.ShowNMoves( _NMoves );
				Game.DoMove( m );

				TeamEnum las_turn = Game.Turn;
				Game.ChangeTurn();
				puntaje = MiniMaxSearch( Game, depth - 1 );
				Game.UndoMove( m );
				Game.Turn = las_turn;

				if( Game.Turn == Team && puntaje > mejor )
				{
					mejor = puntaje;  
					if( depth == Level )
						_MejorJugada = m;
				}
				else if( Game.Turn != Team && puntaje < mejor )
				{
					mejor = puntaje;  
					if( depth == Level )
						_MejorJugada = m;
				}
			}
				
			return mejor;
		}
	}
}

